From: Florian Müllner Date: Thu, 23 Feb 2017 21:50:04 +0000 (+0100) Subject: util: Add AppSettingsMonitor X-Git-Tag: archive/raspbian/50.5-1+rpi1~1^2~1013^2^2~512 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=b5130c5943de6d0f415493d39dd32e9d4210e20a;p=gnome-shell.git util: Add AppSettingsMonitor When integrating with optional components like Clocks, it is not safe to access their GSettings right after the application became visible to the AppSystem: Installation is usually not atomic, so the .desktop file may appear before the settings schema, in which case Gio will abort due to an "invalid" schema ID. To address this, add a small helper class that wraps the settings access in a safe way. https://bugzilla.gnome.org/show_bug.cgi?id=766410 --- diff --git a/js/misc/util.js b/js/misc/util.js index 7c6d11124..0221e15af 100644 --- a/js/misc/util.js +++ b/js/misc/util.js @@ -4,6 +4,8 @@ const Clutter = imports.gi.Clutter; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Lang = imports.lang; +const Mainloop = imports.mainloop; +const Signals = imports.signals; const Shell = imports.gi.Shell; const St = imports.gi.St; @@ -398,3 +400,94 @@ function ensureActorVisibleInScrollView(scrollView, actor) { time: SCROLL_TIME, transition: 'easeOutQuad' }); } + +const AppSettingsMonitor = new Lang.Class({ + Name: 'AppSettingsMonitor', + + _init: function(appId, schemaId) { + this._appId = appId; + this._schemaId = schemaId; + + this._app = null; + this._settings = null; + this._handlers = []; + + this._schemaSource = Gio.SettingsSchemaSource.get_default(); + + this._appSystem = Shell.AppSystem.get_default(); + this._appSystem.connect('installed-changed', + Lang.bind(this, this._onInstalledChanged)); + this._onInstalledChanged(); + }, + + get available() { + return this._app != null && this._settings != null; + }, + + activateApp: function() { + if (this._app) + this._app.activate(); + }, + + watchSetting: function(key, callback) { + let handler = { id: 0, key: key, callback: callback }; + this._handlers.push(handler); + + this._connectHandler(handler); + }, + + _connectHandler: function(handler) { + if (!this._settings || handler.id > 0) + return; + + handler.id = this._settings.connect('changed::' + handler.key, + handler.callback); + handler.callback(this._settings, handler.key); + }, + + _disconnectHandler: function(handler) { + if (this._settings && handler.id > 0) + this._settings.disconnect(handler.id); + handler.id = 0; + }, + + _onInstalledChanged: function() { + let hadApp = (this._app != null); + this._app = this._appSystem.lookup_app(this._appId); + let haveApp = (this._app != null); + + if (hadApp == haveApp) + return; + + if (haveApp) + this._checkSettings(); + else + this._setSettings(null); + }, + + _setSettings(settings) { + this._handlers.forEach((handler) => { this._disconnectHandler(handler); }); + + let hadSettings = (this._settings != null); + this._settings = settings; + let haveSettings = (this._settings != null); + + this._handlers.forEach((handler) => { this._connectHandler(handler); }); + + if (hadSettings != haveSettings) + this.emit('available-changed'); + }, + + _checkSettings: function() { + let schema = this._schemaSource.lookup(this._schemaId, true); + if (schema) { + this._setSettings(new Gio.Settings({ settings_schema: schema })); + } else if (this._app) { + Mainloop.timeout_add_seconds(1, () => { + this._checkSettings(); + return GLib.SOURCE_REMOVE; + }); + } + } +}); +Signals.addSignalMethods(AppSettingsMonitor.prototype);